import Zhi from '../bagua/zhi.js'
import Gan from '../bagua/gan.js'
import Ganzhi from '../bagua/ganzhi.js'
import GuiGod from '../bagua/guiGod.js'
import God12 from '../bagua/god12.js'
import relationSettings from '../relation/relationSettings.js'

import BaseStage from './baseStage.js'

import dictionary from '../bagua/dictionary.js'

/*
  与地支十二宫相关的事物，都归为六壬排布中
*/
class Liuren extends BaseStage {
  constructor(options = {}) {
    super(options)
    
    this.initLiuren()
  }
  
  initLiuren () {
    this.types.push('Liuren')
    if (!this.enabled) {
      return
    }
    
    // 根据用神、贵人阴阳，宿命，计算六壬局
    this.createLiuren(this.options.liurenKeyGanzhi, this.options.liurenShengxiao, this.options.guiGodType)
  }
  
  // 根据用神、贵人阴阳，宿命，计算六壬局
  createLiuren (liurenKeyGanzhi, liurenShengxiao, guiGodType) {
    // 重新设定用神、贵人阴阳，宿命
    this.keyGanzhi = new Ganzhi(liurenKeyGanzhi[0], liurenKeyGanzhi[1], null, {
      description: dictionary.LIUREN_KEY_GANZHI
    })
    this.live = new Zhi(liurenShengxiao, {
      description: dictionary.LIUREN_LIVE
    })
    this.guiGodType = guiGodType
    
    // 初始化地支十二神
    this.zhiPalaces = this.initZhiPalaces()
    
    // 天盘月将，加载时干上
    this.yuejiang = new Zhi(this.date.yuejiang.index, {
      description: dictionary.LIUREN_YUEJIANG,
      yuejiangKey: true
    }) 
    Liuren.setYuejiang(this, 'hour')
    // 贵神加在天盘上，天盘是加在时支上
    const [guiGodIndex, guiGodYinyang] = Liuren.setGuiGods(this, `${this.guiGodType} ${this.keyGanzhi.name} sky hour`)
    this.guiGod = new Zhi(guiGodIndex, {
      description: dictionary.LIUREN_GUIGOD
    })
    this.guiGodYinyang = guiGodYinyang
    // 排外天干
    Liuren.setLiurenGan(this, `liuren, ${this.keyGanzhi.name}, hour`)
    // 定空亡(定局干支空亡)
    this.timeGod.initKeyEmpty(this.keyGanzhi)
    
    // 起三传，时运命
    this.setLegend()
    this.setDestiny()
  }
  
  // 生肖改变仅仅触发 时运命 的重新计算
  updateDestiny (liurenShengxiao) {
    this.live = new Zhi(liurenShengxiao, {
      description: dictionary.LIUREN_LIVE
    })
    this.setDestiny()
  }
  
  isFuyin () {
    if (!this.zhiPalaces || !this.zhiPalaces[0].ele.yuejiang) {
      throw new Error('六壬局尚未排完，无法判断伏吟!')
    }
    
    return this.zhiPalaces[0].ele.zhi.index === this.zhiPalaces[0].ele.yuejiang.index
  }
  
  // 排三传
  setLegend (liuren) {
    this.ganLegend = [this.keyGanzhi.gan]
    for (let i = 0; i < 3; i++) {
      let zhiIndex = this.getNextLegend(this.ganLegend[i], i)
      this.ganLegend.push(new Zhi(zhiIndex, {
        description: dictionary.GAN + dictionary.LIUREN_LEGEND
      }))
    }

    this.zhiLegend = [this.keyGanzhi.zhi]
    for (var i = 0; i < 3; i++) {
      let zhiIndex = this.getNextLegend(this.zhiLegend[i], i)
      this.zhiLegend.push(new Zhi(zhiIndex, {
        description: dictionary.ZHI + dictionary.LIUREN_LEGEND
      }))
    }
  }
  
  // 获取下一个三传，返回地支序数
  getNextLegend (ele, index) {
    // 天干判断
    const isGanEle = ele.types.indexOf('Gan') >= 0
    
    let zhiIndex = isGanEle ? Gan.liurenZhiList[ele.index] : ele.index
    
    if (this.isFuyin()) {
      if (index === 0) {
        // 天干直接返回寄宫地支
        return zhiIndex
      }
      
      // 地支伏吟取相刑，自刑取相冲
      let res = Zhi.zhiList.indexOf(relationSettings.xing.list[zhiIndex])
      if (res === zhiIndex) {
        res = Zhi.zhiList.indexOf(relationSettings.zhiChong.list[zhiIndex])
      }
      return res
    }
    else {
      return this.zhiPalaces[zhiIndex].ele.yuejiang.index
    }
  }

  // 排时运命
  setDestiny () {
    this.destiny = {
      time: new Zhi(this.date.ganzhi[3].zhi.index, {
        description: dictionary.LIUREN_SEASON
      }),
      destiny: new Zhi(this.date.yuejiang.index, {
        description: dictionary.LIUREN_DESTINY
      }),
      live: this.live
    }
  }
  
  /* @section static静态方法 */
  /*
    排天盘月将，因为遁甲涉及穿壬，所以写成静态方法
    type = 空串 月将加载时支上，六壬起局用
    type = key 月将加载定局地支上
    type = hour/day/month/year 月将加载时支/日支/月支/年支上，以此类推
    
    eleKey 加入对象后的指定key，默认为yuejiang
  */
  static setYuejiang (stage, type = '', eleKey = 'yuejiang') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    const len = Zhi.zhiList.length
    // 月将加的初始位置
    const zhiStartIndex = type === '' ? 
      stage.date.ganzhi[3].zhi.index :
      Liuren.getZhiStartByTimeKey(stage, type)
    if (zhiStartIndex < 0) { return }
    // 月将的初始索引
    let zhiYuejiangIndex = stage.date.yuejiang.index
    stage.traveZhiByClock(zhiStartIndex, len, (item, index) => {
      item.ele[eleKey] = new Zhi(zhiYuejiangIndex, {
        description: item.description + dictionary.SKY_ZHI,
        yuejiangKey: true
      })
      
      zhiYuejiangIndex = (zhiYuejiangIndex + 1) % len
      return item
    })
  }
  
  /*
    最常规的十二建除排法，六壬、遁甲穿壬
    type = key/hour/day/month/year 因为六壬中十二建并不是起局基础，所以没有空串的默认情况
    其他参数同上
  */
  static setJianchu (stage, type = 'key', eleKey = 'jianchu') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    const len = God12.godList.jianchu.list.length
    const zhiStartIndex = Liuren.getZhiStartByTimeKey(stage, type)
    if (zhiStartIndex < 0) { return }
    
    stage.traveZhiByClock(zhiStartIndex, len, (item, index) => {
      item.ele[eleKey] = new God12(index, 'jianchu', {
        description: item.description + dictionary.JIANCHU,
      })
      
      return item
    })
  }
  
  /*
    排十二宫
    月将加载时支宫上，逆时针行宫，顺排月将，直到卯宫，此时数到的地支就是命宫的位置，阳顺阴逆排12宫
    参数意义同十二建
  */
  static setTwelvePalaces (stage, type = 'key', eleKey = 'twelvePalace') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    // 逆数到卯
    const desPosIndex = 3
    const len = Zhi.zhiList.length
    const zhiStartIndex = Liuren.getZhiStartByTimeKey(stage, type)
    if (zhiStartIndex < 0) { return }
    
    const zhiOffset = (zhiStartIndex - desPosIndex + len) % len
    // 命宫的位置
    const startIndex = (stage.date.yuejiang.index + zhiOffset) % len
    // 注意阳顺阴逆
    const offset = stage.isSolar ? len : (-1) * len
    
    stage.traveZhiByClock(startIndex, offset, (item, index) => {
      item.ele[eleKey] = new God12(index, 'twelvePalace', {
        description: item.description + dictionary.TWELVEPALACE,
      })
      
      return item
    })
  }
  
  /*
    排十二神将
    type = auto day 六壬起局时的默认参数，自动分阴阳贵人，以日干支为用神
    type = 0 1 2 3 遁甲穿壬，一共四个参数，需要详细解析
    type[0] 阴阳贵人 auto自动 solar阳贵人 lunar阴贵人
    type[1] 如何找贵人，确定贵人用神，可以直接传入用神天干，也可以传入year/month/day/hour/min代表 年~时 天干
    type[2] 判断贵人是加在地盘还是天盘，ground排在地盘，sky排在天盘。六壬排盘贵人固定加天盘，此参数为空
    type[3] 在type[2]确定排在天盘的情况下，此参数指定天盘起排的地支位
    返回贵人索引
  */
  static setGuiGods (stage, type = 'auto day', eleKey = 'guiGod') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    const len = Zhi.zhiList.length
    // 空格分离，确认所有设定
    let typeList = type.split(' ')
    
    // type[0] 首先确定阴阳贵人
    if (typeList[0] === 'auto') {
      const timeZhiIndex = stage.date.ganzhi[3].zhi.index
      if (timeZhiIndex >= 3 && timeZhiIndex <= 8) {
        // 卯酉时分定阴阳，卯到申为阳
        typeList[0] = 'solar'
      }
      else {
        typeList[0] = 'lunar'
      }
    }
    const yinyangGuiGod = typeList[0] === 'solar' ? 0 : 1
    
    let ganzhi
    // type[1] 确定贵人所在地支位，直接传入天干的情况
    if (Gan.ganList.indexOf(typeList[1][0])>= 0) {
      // 用神直接生成干支
      ganzhi = new Ganzhi(typeList[1][0], typeList[1][1])
    }
    else {
      // 从四柱获取干支
      ganzhi = Liuren.getGanzhiByTimeKey(stage, typeList[1])
    }
    if (!ganzhi) {
      return
    }
    // 获取贵人索引
    const guiGodIndexList = stage.timeGod.guiGod(ganzhi, false)
    const zhiPosIndex = guiGodIndexList[yinyangGuiGod]
    
    // type[2] type[3] 共同确定贵人起排点
    // type[2] = ground时，排在地盘
    let zhiStartIndex = zhiPosIndex
    if (typeList[2] !== 'ground') {
      // 六壬排盘默认是以时干起天盘，即月将
      let yuejiangZhiStart = stage.date.ganzhi[3].zhi.index
      if (typeList[2] === 'sky' && typeList[3]) {
        // 穿壬时，要从新确认月将的起排点
        yuejiangZhiStart = Liuren.getZhiStartByTimeKey(stage, typeList[3])
      }
      if (yuejiangZhiStart < 0) { return }
      const yuejiangZhiIndex = stage.date.yuejiang.index
      const yuejiangOffset = zhiPosIndex >= yuejiangZhiIndex ? zhiPosIndex - yuejiangZhiIndex : zhiPosIndex + len - yuejiangZhiIndex
      zhiStartIndex = (yuejiangZhiStart + yuejiangOffset) % len
    }
    
    // 落在亥到辰顺排，落在巳到戌逆排
    const offset = zhiStartIndex >= 5 && zhiStartIndex <= 10 ? (-1) * len : len
    
    stage.traveZhiByClock(zhiStartIndex, offset, (item, index) => {
      item.ele[eleKey] = new GuiGod(index, {
        description: item.description + dictionary.GUIGOD,
      })
      
      return item
    })
    
    return [zhiPosIndex, yinyangGuiGod]
  }
  
  /*
    排太阴
    type = key/hour/day/month/year 因为六壬中太阴并不是起局基础，所以没有空串的默认情况
    其他参数同上
  */
  static setTaiyin (stage, type = 'key', eleKey = 'taiyin') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    const len = Zhi.zhiList.length
    const zhiStartIndex = Liuren.getZhiStartByTimeKey(stage, type)
    let zhiTaiyinIndex = stage.timeGod.taiyin.index
    
    stage.traveZhiByClock(zhiStartIndex, len * (-1), (item, index) => {
      item.ele[eleKey] = new Zhi(zhiTaiyinIndex, {
        description: item.description + dictionary.TAIYIN,
      })
      zhiTaiyinIndex = (zhiTaiyinIndex + 1) % len
      return item
    })
  }
  
  /*
    排天干
    type = liuren day 六壬起局时的默认参数，自动分阴阳贵人，以日干支为用神
    type = 0 1 2 遁甲穿壬，一共三个参数，需要详细解析
    type[0] dunjia为穿壬排天干，liuren为六壬排天干
    type[1] 天干的用神参照，可以直接传入用神干支，也可以传入key year/month/day/hour 从四柱上取
    type[2] 在type[0]为liuren的情况下，天干要加在天盘上，这里是天盘起排的地支位
  */
  static setLiurenGan (stage, type = 'liuren day', eleKey = 'liurenGan') {
    if (stage.zhiPalaces[0].ele.hasOwnProperty(eleKey)) {
      return
    }
    
    const len = Zhi.zhiList.length
    const ganLen = Gan.ganList.length
    let typeList = type.split(' ')
    
    if (typeList[0] === 'dunjia') {
      // 遁甲式排天干
      // 地支为子，在日干用五鼠遁推出起始天干
      let startGanIndex = stage.date.ganzhi[2].gan.getMouseGanIndex(0)
      stage.traveZhiByClock(0, len, (item, index) => {
        item.ele[eleKey] = new Gan(startGanIndex, {
          description: item.description + dictionary.LIURENGAN,
        })
        startGanIndex = (startGanIndex + 1) % ganLen
        return item
      })
      return
    }
    
    // type[1] 六壬式排天干确认用神
    let ganzhi
    // type[1] 确定贵人所在地支位，直接传入天干的情况
    if (Gan.ganList.indexOf(typeList[1][0])>= 0) {
      // 用神直接生成干支
      ganzhi = new Ganzhi(typeList[1][0], typeList[1][1])
    }
    else {
      // 从四柱获取干支
      ganzhi = Liuren.getGanzhiByTimeKey(stage, typeList[1])
    }
    if (!ganzhi) {
      return
    }
    // 甲对应的地支
    const zhiPosIndex = ganzhi.zhi.prevIndex(ganzhi.gan.index)
    
    // type[2] 确定天干甲起排点
    // 默认为时支
    let yuejiangZhiStart = stage.date.ganzhi[3].zhi.index
    if (typeList[2]) {
      yuejiangZhiStart = Liuren.getZhiStartByTimeKey(stage, typeList[2])
    }
    if (yuejiangZhiStart < 0) { return }
    const yuejiangZhiIndex = stage.date.yuejiang.index
    const yuejiangOffset = zhiPosIndex >= yuejiangZhiIndex ? zhiPosIndex - yuejiangZhiIndex : zhiPosIndex + len - yuejiangZhiIndex
    const zhiStartIndex = (yuejiangZhiStart + yuejiangOffset) % len
    
    stage.traveZhiByClock(zhiStartIndex, len, (item, index) => {
      item.ele[eleKey] = new Gan(index % ganLen, {
        description: item.description + dictionary.LIURENGAN,
      })
      return item
    })
  }
}

export default Liuren